package com.zyh.system.supermark1.db;

import com.zyh.domain.PageInfo;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author zyh
 * @Description 这是一个专门用于数据库操作的工具类，功能比 JDBCUtil 更加强大
 * 特点：
 * 1.所有的表都适用，每次操作前只需要将 db.properties 中的 url 的数据修改
 * 2.只需要传入 sql ，用于封装表中数据的字节码对象，sql 中的 ? 占位符参数，返回数据集合或者影响的行数
 */

public final class BaseDAO {
    private BaseDAO() {
    }

    // 1. 向表中添加删除修改一行数据
    public static int changeOneData(String sql, Object... params) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            // 参数校验
            if (sql == null || sql.length() == 0 || "".equals(sql)) {
                throw new IllegalArgumentException("sql语句异常");
            }
            // 获取连接
            connection = JDBCUtil.getConnection();
            // 获取预编译的发送和执行 sql 语句的对象
            ps = connection.prepareStatement(sql);
            // 对可变参数进行遍历
            for (int i = 0; i < params.length; i++) {
                // 将实际参数替换为 sql 中的 ? 号占位符
                ps.setObject((i + 1), params[i]);
            }
            // 发送和执行 sql 并拿到影响的行数
            int count = ps.executeUpdate();
            // 返回影响的行数
            return count;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(ps, connection);
        }
        return 0;
    }

    // 2. 根据 id 批量删除数据
    public static int deleteMultipleData(String sql, Integer... ids) {
        Connection connection = null;
        PreparedStatement ps = null;
        try {
            // 参数校验
            if (sql == null || sql.length() == 0 || "".equals(sql)) {
                throw new IllegalArgumentException("sql语句异常");
            }
            // 获取连接
            connection = JDBCUtil.getConnection();
            // 获取预编译对象
            ps = connection.prepareStatement(sql);
            // 关闭自动提交事务
            connection.setAutoCommit(false);
            // 遍历 ids
            for (Integer id : ids) {
                // 替换 sql 中的 ? 号占位符
                ps.setObject(1, id);
                // 将每一次封装好的 ps 对象存入事务容器
                ps.addBatch();
            }
            // 将容器中的 ps 对象进行提交
            int[] arr = ps.executeBatch();
            // 拿到结果
            int count = 0;
            for (int item : arr) {
                if (item == 1) {
                    count++;
                } else {
                    throw new SQLException("删除 id 有误");
                }
            }
            // 提交事务
            connection.commit();
            return count;
        } catch (SQLException e) {
            // 回滚事务
            try {
                connection.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JDBCUtil.close(ps, connection);
        }
        return -1;
    }

    // 3. 查询某数据库的一张表的数据
    public static <T> List<T> selectData(String sql, Class<T> tClass, Object... params) {
        Connection connection = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 参数校验
            if (sql == null || sql.length() == 0 || "".equals(sql) || tClass == null) {
                throw new IllegalArgumentException("非法参数异常");
            }
            // 创建一个 List 集合用来存放查询出的每一列对象信息
            List<T> list = new ArrayList<>();
            // 获取连接
            connection = JDBCUtil.getConnection();
            // 获取预编译对象
            ps = connection.prepareStatement(sql);
            // 用实际参数替换 sql 中的 ? 占位符
            for (int i = 0; i < params.length; i++) {
                ps.setObject((i + 1), params[i]);
            }
            // 执行 sql 语句
            rs = ps.executeQuery();
            // 获取表的标题
            ResultSetMetaData metaData = rs.getMetaData();
            // 获取表的列数
            int columnCount = metaData.getColumnCount();
            // 获取每一行的数据
            while (rs.next()) {
                // 创建一个空的 t 对象
                T t = tClass.getDeclaredConstructor().newInstance();
                // 循环列的数量
                for (int i = 1; i <= columnCount; i++) {
                    // 拿到每一列的名字
                    String columnName = metaData.getColumnName(i);
                    // 拿到每一列的值
                    Object columnValue = rs.getObject(i);
                    // 获取 t 对象的属性
                    Field declaredField = tClass.getDeclaredField(columnName);
                    // 打开访问开关，并将 columnValue 值赋给 t 对象
                    declaredField.setAccessible(true);
                    declaredField.set(t, columnValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtil.close(rs, ps, connection);
        }
        return null;
    }

    // 4. 分页查询数据
    public static <T> PageInfo<T> selectByPageNum(String sql, Class<T> tClass, Integer pageNum, Integer pageSize, Object... params) {
        // 参数校验
        if (sql == null || sql.length() == 0 || "".equals(sql) || tClass == null) {
            throw new IllegalArgumentException("非法参数异常");
        }
        // 改装 sql 拼接 limit
//        sql=sql + " limit "+(pageNum-1)*pageSize +","+pageSize ;
        sql = sql + " limit " + (pageNum - 1) * pageSize + " , " + pageSize;
        // 调用 selectData() 方法
        List<T> list = selectData(sql, tClass, params);
        PageInfo<T> tPageInfo = new PageInfo<>(list, list.size());
        return tPageInfo;
    }
}
